home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkXEvent.c < prev    next >
C/C++ Source or Header  |  1995-05-17  |  29KB  |  998 lines

  1. /* 
  2.  * tkXEvent.c --
  3.  *
  4.  *    This file provides basic low-level facilities for managing
  5.  *    X events.  It builds on the facilities provided in tkEvent.c.
  6.  *
  7.  * Copyright (c) 1990-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  */
  13.  
  14. static char sccsid[] = "@(#) tkXEvent.c 1.8 95/05/17 10:49:43";
  15.  
  16. #include "tkPort.h"
  17. #include "tkInt.h"
  18. #include <signal.h>
  19.  
  20. /*
  21.  * There's a potential problem if a handler is deleted while it's
  22.  * current (i.e. its procedure is executing), since Tk_HandleEvent
  23.  * will need to read the handler's "nextPtr" field when the procedure
  24.  * returns.  To handle this problem, structures of the type below
  25.  * indicate the next handler to be processed for any (recursively
  26.  * nested) dispatches in progress.  The nextHandler fields get
  27.  * updated if the handlers pointed to are deleted.  Tk_HandleEvent
  28.  * also needs to know if the entire window gets deleted;  the winPtr
  29.  * field is set to zero if that particular window gets deleted.
  30.  */
  31.  
  32. typedef struct InProgress {
  33.     XEvent *eventPtr;         /* Event currently being handled. */
  34.     TkWindow *winPtr;         /* Window for event.  Gets set to None if
  35.                   * window is deleted while event is being
  36.                   * handled. */
  37.     TkEventHandler *nextHandler; /* Next handler in search. */
  38.     struct InProgress *nextPtr;     /* Next higher nested search. */
  39. } InProgress;
  40.  
  41. static InProgress *pendingPtr = NULL;
  42.                 /* Topmost search in progress, or
  43.                  * NULL if none. */
  44.  
  45. /*
  46.  * For each call to Tk_CreateGenericHandler, an instance of the following
  47.  * structure will be created.  All of the active handlers are linked into a
  48.  * list.
  49.  */
  50.  
  51. typedef struct GenericHandler {
  52.     Tk_GenericProc *proc;    /* Procedure to dispatch on all X events. */
  53.     ClientData clientData;    /* Client data to pass to procedure. */
  54.     int deleteFlag;        /* Flag to set when this handler is deleted. */
  55.     struct GenericHandler *nextPtr;
  56.                 /* Next handler in list of all generic
  57.                  * handlers, or NULL for end of list. */
  58. } GenericHandler;
  59.  
  60. static GenericHandler *genericList = NULL;
  61.                 /* First handler in the list, or NULL. */
  62. static GenericHandler *lastGenericPtr = NULL;
  63.                 /* Last handler in list. */
  64.  
  65. /*
  66.  * There's a potential problem if Tk_HandleEvent is entered recursively.
  67.  * A handler cannot be deleted physically until we have returned from
  68.  * calling it.  Otherwise, we're looking at unallocated memory in advancing to
  69.  * its `next' entry.  We deal with the problem by using the `delete flag' and
  70.  * deleting handlers only when it's known that there's no handler active.
  71.  *
  72.  * The following variable has a non-zero value when a handler is active.
  73.  */
  74.  
  75. static int genericHandlersActive = 0;
  76.  
  77. /*
  78.  * Array of event masks corresponding to each X event:
  79.  */
  80.  
  81. static unsigned long eventMasks[] = {
  82.     0,
  83.     0,
  84.     KeyPressMask,            /* KeyPress */
  85.     KeyReleaseMask,            /* KeyRelease */
  86.     ButtonPressMask,            /* ButtonPress */
  87.     ButtonReleaseMask,            /* ButtonRelease */
  88.     PointerMotionMask|PointerMotionHintMask|ButtonMotionMask
  89.         |Button1MotionMask|Button2MotionMask|Button3MotionMask
  90.         |Button4MotionMask|Button5MotionMask,
  91.                     /* MotionNotify */
  92.     EnterWindowMask,            /* EnterNotify */
  93.     LeaveWindowMask,            /* LeaveNotify */
  94.     FocusChangeMask,            /* FocusIn */
  95.     FocusChangeMask,            /* FocusOut */
  96.     KeymapStateMask,            /* KeymapNotify */
  97.     ExposureMask,            /* Expose */
  98.     ExposureMask,            /* GraphicsExpose */
  99.     ExposureMask,            /* NoExpose */
  100.     VisibilityChangeMask,        /* VisibilityNotify */
  101.     SubstructureNotifyMask,        /* CreateNotify */
  102.     StructureNotifyMask,        /* DestroyNotify */
  103.     StructureNotifyMask,        /* UnmapNotify */
  104.     StructureNotifyMask,        /* MapNotify */
  105.     SubstructureRedirectMask,        /* MapRequest */
  106.     StructureNotifyMask,        /* ReparentNotify */
  107.     StructureNotifyMask,        /* ConfigureNotify */
  108.     SubstructureRedirectMask,        /* ConfigureRequest */
  109.     StructureNotifyMask,        /* GravityNotify */
  110.     ResizeRedirectMask,            /* ResizeRequest */
  111.     StructureNotifyMask,        /* CirculateNotify */
  112.     SubstructureRedirectMask,        /* CirculateRequest */
  113.     PropertyChangeMask,            /* PropertyNotify */
  114.     0,                    /* SelectionClear */
  115.     0,                    /* SelectionRequest */
  116.     0,                    /* SelectionNotify */
  117.     ColormapChangeMask,            /* ColormapNotify */
  118.     0,                    /* ClientMessage */
  119.     0,                    /* Mapping Notify */
  120. };
  121.  
  122. /*
  123.  * If someone has called Tk_RestrictEvents, the information below
  124.  * keeps track of it.
  125.  */
  126.  
  127. static Bool (*restrictProc)  _ANSI_ARGS_((Display *display, XEvent *eventPtr,
  128.     char *arg));        /* Procedure to call.  NULL means no
  129.                  * restrictProc is currently in effect. */
  130. static char *restrictArg;    /* Argument to pass to restrictProc. */
  131.  
  132. /*
  133.  * The following array keeps track of the last TK_NEVENTS X events, for
  134.  * memory dump analysis.  The tracing is only done if tkEventDebug is set
  135.  * to 1.
  136.  */
  137.  
  138. #define TK_NEVENTS 32
  139. static XEvent eventTrace[TK_NEVENTS];
  140. static int traceIndex = 0;
  141. int tkEventDebug = 0;
  142.  
  143. /*
  144.  * A mouse motion event whose processing is delayed, in order to
  145.  * merge it with subsequent mouse motion events, if possible:
  146.  */
  147.  
  148. static XEvent delayedMotionEvent;
  149.  
  150. /*
  151.  * Prototypes for procedures referenced only in this file:
  152.  */
  153.  
  154. static void        DelayedEventProc _ANSI_ARGS_((void));
  155.  
  156. /*
  157.  *--------------------------------------------------------------
  158.  *
  159.  * Tk_CreateEventHandler --
  160.  *
  161.  *    Arrange for a given procedure to be invoked whenever
  162.  *    events from a given class occur in a given window.
  163.  *
  164.  * Results:
  165.  *    None.
  166.  *
  167.  * Side effects:
  168.  *    From now on, whenever an event of the type given by
  169.  *    mask occurs for token and is processed by Tk_HandleEvent,
  170.  *    proc will be called.  See the manual entry for details
  171.  *    of the calling sequence and return value for proc.
  172.  *
  173.  *--------------------------------------------------------------
  174.  */
  175.  
  176. void
  177. Tk_CreateEventHandler(token, mask, proc, clientData)
  178.     Tk_Window token;        /* Token for window in which to
  179.                  * create handler. */
  180.     unsigned long mask;        /* Events for which proc should
  181.                  * be called. */
  182.     Tk_EventProc *proc;        /* Procedure to call for each
  183.                  * selected event */
  184.     ClientData clientData;    /* Arbitrary data to pass to proc. */
  185. {
  186.     register TkEventHandler *handlerPtr;
  187.     register TkWindow *winPtr = (TkWindow *) token;
  188.     int found;
  189.  
  190.     /*
  191.      * Skim through the list of existing handlers to (a) compute the
  192.      * overall event mask for the window (so we can pass this new
  193.      * value to the X system) and (b) see if there's already a handler
  194.      * declared with the same callback and clientData (if so, just
  195.      * change the mask).  If no existing handler matches, then create
  196.      * a new handler.
  197.      */
  198.  
  199.     found = 0;
  200.     if (winPtr->handlerList == NULL) {
  201.     handlerPtr = (TkEventHandler *) ckalloc(
  202.         (unsigned) sizeof(TkEventHandler));
  203.     winPtr->handlerList = handlerPtr;
  204.     goto initHandler;
  205.     } else {
  206.     for (handlerPtr = winPtr->handlerList; ;
  207.         handlerPtr = handlerPtr->nextPtr) {
  208.         if ((handlerPtr->proc == proc)
  209.             && (handlerPtr->clientData == clientData)) {
  210.         handlerPtr->mask = mask;
  211.         found = 1;
  212.         }
  213.         if (handlerPtr->nextPtr == NULL) {
  214.         break;
  215.         }
  216.     }
  217.     }
  218.  
  219.     /*
  220.      * Create a new handler if no matching old handler was found.
  221.      */
  222.  
  223.     if (!found) {
  224.     handlerPtr->nextPtr = (TkEventHandler *)
  225.         ckalloc(sizeof(TkEventHandler));
  226.     handlerPtr = handlerPtr->nextPtr;
  227.     initHandler:
  228.     handlerPtr->mask = mask;
  229.     handlerPtr->proc = proc;
  230.     handlerPtr->clientData = clientData;
  231.     handlerPtr->nextPtr = NULL;
  232.     }
  233.  
  234.     /*
  235.      * No need to call XSelectInput:  Tk always selects on all events
  236.      * for all windows (needed to support bindings on classes and "all").
  237.      */
  238. }
  239.  
  240. /*
  241.  *--------------------------------------------------------------
  242.  *
  243.  * Tk_DeleteEventHandler --
  244.  *
  245.  *    Delete a previously-created handler.
  246.  *
  247.  * Results:
  248.  *    None.
  249.  *
  250.  * Side effects:
  251.  *    If there existed a handler as described by the
  252.  *    parameters, the handler is deleted so that proc
  253.  *    will not be invoked again.
  254.  *
  255.  *--------------------------------------------------------------
  256.  */
  257.  
  258. void
  259. Tk_DeleteEventHandler(token, mask, proc, clientData)
  260.     Tk_Window token;        /* Same as corresponding arguments passed */
  261.     unsigned long mask;        /* previously to Tk_CreateEventHandler. */
  262.     Tk_EventProc *proc;
  263.     ClientData clientData;
  264. {
  265.     register TkEventHandler *handlerPtr;
  266.     register InProgress *ipPtr;
  267.     TkEventHandler *prevPtr;
  268.     register TkWindow *winPtr = (TkWindow *) token;
  269.  
  270.     /*
  271.      * Find the event handler to be deleted, or return
  272.      * immediately if it doesn't exist.
  273.      */
  274.  
  275.     for (handlerPtr = winPtr->handlerList, prevPtr = NULL; ;
  276.         prevPtr = handlerPtr, handlerPtr = handlerPtr->nextPtr) {
  277.     if (handlerPtr == NULL) {
  278.         return;
  279.     }
  280.     if ((handlerPtr->mask == mask) && (handlerPtr->proc == proc)
  281.         && (handlerPtr->clientData == clientData)) {
  282.         break;
  283.     }
  284.     }
  285.  
  286.     /*
  287.      * If Tk_HandleEvent is about to process this handler, tell it to
  288.      * process the next one instead.
  289.      */
  290.  
  291.     for (ipPtr = pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
  292.     if (ipPtr->nextHandler == handlerPtr) {
  293.         ipPtr->nextHandler = handlerPtr->nextPtr;
  294.     }
  295.     }
  296.  
  297.     /*
  298.      * Free resources associated with the handler.
  299.      */
  300.  
  301.     if (prevPtr == NULL) {
  302.     winPtr->handlerList = handlerPtr->nextPtr;
  303.     } else {
  304.     prevPtr->nextPtr = handlerPtr->nextPtr;
  305.     }
  306.     ckfree((char *) handlerPtr);
  307.  
  308.  
  309.     /*
  310.      * No need to call XSelectInput:  Tk always selects on all events
  311.      * for all windows (needed to support bindings on classes and "all").
  312.      */
  313. }
  314.  
  315. /*--------------------------------------------------------------
  316.  *
  317.  * Tk_CreateGenericHandler --
  318.  *
  319.  *    Register a procedure to be called on each X event, regardless
  320.  *    of display or window.  Generic handlers are useful for capturing
  321.  *    events that aren't associated with windows, or events for windows
  322.  *    not managed by Tk.
  323.  *
  324.  * Results:
  325.  *    None.
  326.  *
  327.  * Side Effects:
  328.  *    From now on, whenever an X event is given to Tk_HandleEvent,
  329.  *    invoke proc, giving it clientData and the event as arguments.
  330.  *
  331.  *--------------------------------------------------------------
  332.  */
  333.  
  334. void
  335. Tk_CreateGenericHandler(proc, clientData)
  336.      Tk_GenericProc *proc;    /* Procedure to call on every event. */
  337.      ClientData clientData;    /* One-word value to pass to proc. */
  338. {
  339.     GenericHandler *handlerPtr;
  340.     
  341.     handlerPtr = (GenericHandler *) ckalloc (sizeof (GenericHandler));
  342.     
  343.     handlerPtr->proc = proc;
  344.     handlerPtr->clientData = clientData;
  345.     handlerPtr->deleteFlag = 0;
  346.     handlerPtr->nextPtr = NULL;
  347.     if (genericList == NULL) {
  348.     genericList = handlerPtr;
  349.     } else {
  350.     lastGenericPtr->nextPtr = handlerPtr;
  351.     }
  352.     lastGenericPtr = handlerPtr;
  353. }
  354.  
  355. /*
  356.  *--------------------------------------------------------------
  357.  *
  358.  * Tk_DeleteGenericHandler --
  359.  *
  360.  *    Delete a previously-created generic handler.
  361.  *
  362.  * Results:
  363.  *    None.
  364.  *
  365.  * Side Effects:
  366.  *    If there existed a handler as described by the parameters,
  367.  *    that handler is logically deleted so that proc will not be
  368.  *    invoked again.  The physical deletion happens in the event
  369.  *    loop in Tk_HandleEvent.
  370.  *
  371.  *--------------------------------------------------------------
  372.  */
  373.  
  374. void
  375. Tk_DeleteGenericHandler(proc, clientData)
  376.      Tk_GenericProc *proc;
  377.      ClientData clientData;
  378. {
  379.     GenericHandler * handler;
  380.     
  381.     for (handler = genericList; handler; handler = handler->nextPtr) {
  382.     if ((handler->proc == proc) && (handler->clientData == clientData)) {
  383.         handler->deleteFlag = 1;
  384.     }
  385.     }
  386. }
  387.  
  388. /*
  389.  *--------------------------------------------------------------
  390.  *
  391.  * Tk_HandleEvent --
  392.  *
  393.  *    Given an event, invoke all the handlers that have
  394.  *    been registered for the event.
  395.  *
  396.  * Results:
  397.  *    None.
  398.  *
  399.  * Side effects:
  400.  *    Depends on the handlers.
  401.  *
  402.  *--------------------------------------------------------------
  403.  */
  404.  
  405. void
  406. Tk_HandleEvent(eventPtr)
  407.     XEvent *eventPtr;        /* Event to dispatch. */
  408. {
  409.     register TkEventHandler *handlerPtr;
  410.     register GenericHandler *genericPtr;
  411.     register GenericHandler *genPrevPtr;
  412.     TkWindow *winPtr;
  413.     unsigned long mask;
  414.     InProgress ip;
  415.     Window handlerWindow;
  416.     TkDisplay *dispPtr;
  417.  
  418.     /*
  419.      * First off, look for a special trigger event left around by the
  420.      * grab module.  If it's found, call the grab module and discard
  421.      * the event.
  422.      */
  423.  
  424.     if ((eventPtr->xany.type == -1) && (eventPtr->xany.window == None)) {
  425.     TkGrabTriggerProc(eventPtr);
  426.     return;
  427.     }
  428.  
  429.     /* 
  430.      * Next, invoke all the generic event handlers (those that are
  431.      * invoked for all events).  If a generic event handler reports that
  432.      * an event is fully processed, go no further.
  433.      */
  434.  
  435.     for (genPrevPtr = NULL, genericPtr = genericList;  genericPtr != NULL; ) {
  436.     if (genericPtr->deleteFlag) {
  437.         if (!genericHandlersActive) {
  438.         GenericHandler *tmpPtr;
  439.  
  440.         /*
  441.          * This handler needs to be deleted and there are no
  442.          * calls pending through the handler, so now is a safe
  443.          * time to delete it.
  444.          */
  445.  
  446.         tmpPtr = genericPtr->nextPtr;
  447.         if (genPrevPtr == NULL) {
  448.             genericList = tmpPtr;
  449.         } else {
  450.             genPrevPtr->nextPtr = tmpPtr;
  451.         }
  452.         if (tmpPtr == NULL) {
  453.             lastGenericPtr = genPrevPtr;
  454.         }
  455.         (void) ckfree((char *) genericPtr);
  456.         genericPtr = tmpPtr;
  457.         continue;
  458.         }
  459.     } else {
  460.         int done;
  461.  
  462.         genericHandlersActive++;
  463.         done = (*genericPtr->proc)(genericPtr->clientData, eventPtr);
  464.         genericHandlersActive--;
  465.         if (done) {
  466.         return;
  467.         }
  468.     }
  469.     genPrevPtr = genericPtr;
  470.     genericPtr = genPrevPtr->nextPtr;
  471.     }
  472.  
  473.     /*
  474.      * If the event is a MappingNotify event, find its display and
  475.      * refresh the keyboard mapping information for the display.
  476.      * After that there's nothing else to do with the event, so just
  477.      * quit.
  478.      */
  479.  
  480.     if (eventPtr->type == MappingNotify) {
  481.     dispPtr = TkGetDisplay(eventPtr->xmapping.display);
  482.     if (dispPtr != NULL) {
  483.         XRefreshKeyboardMapping(&eventPtr->xmapping);
  484.         dispPtr->bindInfoStale = 1;
  485.     }
  486.     return;
  487.     }
  488.  
  489.     /*
  490.      * Events selected by StructureNotify require special handling.
  491.      * They look the same as those selected by SubstructureNotify.
  492.      * The only difference is whether the "event" and "window" fields
  493.      * are the same.  Compare the two fields and convert StructureNotify
  494.      * to SubstructureNotify if necessary.
  495.      */
  496.  
  497.     handlerWindow = eventPtr->xany.window;
  498.     mask = eventMasks[eventPtr->xany.type];
  499.     if (mask == StructureNotifyMask) {
  500.     if (eventPtr->xmap.event != eventPtr->xmap.window) {
  501.         mask = SubstructureNotifyMask;
  502.         handlerWindow = eventPtr->xmap.event;
  503.     }
  504.     }
  505.     winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, handlerWindow);
  506.     if (winPtr == NULL) {
  507.  
  508.     /*
  509.      * There isn't a TkWindow structure for this window.
  510.      * However, if the event is a PropertyNotify event then call
  511.      * the selection manager (it deals beneath-the-table with
  512.      * certain properties).
  513.      */
  514.  
  515.     if (eventPtr->type == PropertyNotify) {
  516.         TkSelPropProc(eventPtr);
  517.     }
  518.     return;
  519.     }
  520.  
  521.     if (winPtr->mainPtr != NULL) {
  522.     /*
  523.      * Call focus-related code to look at FocusIn, FocusOut, Enter,
  524.      * and Leave events;  depending on its return value, ignore the
  525.      * event.
  526.      */
  527.     
  528.     if ((mask & (FocusChangeMask|EnterWindowMask|LeaveWindowMask))
  529.         && !TkFocusFilterEvent(winPtr, eventPtr)) {
  530.         return;
  531.     }
  532.     
  533.     /*
  534.      * Redirect KeyPress and KeyRelease events to the focus window,
  535.      * or ignore them entirely if there is no focus window.  Map the
  536.      * x and y coordinates to make sense in the context of the focus
  537.      * window, if possible (make both -1 if the map-from and map-to
  538.      * windows don't share the same screen).
  539.      */
  540.     
  541.     if (mask & (KeyPressMask|KeyReleaseMask)) {
  542.         TkWindow *focusPtr;
  543.         int winX, winY, focusX, focusY;
  544.     
  545.         winPtr->dispPtr->lastEventTime = eventPtr->xkey.time;
  546.         focusPtr = TkGetFocus(winPtr);
  547.         if (focusPtr == NULL) {
  548.         return;
  549.         }
  550.         if ((focusPtr->display != winPtr->display)
  551.             || (focusPtr->screenNum != winPtr->screenNum)) {
  552.         eventPtr->xkey.x = -1;
  553.         eventPtr->xkey.y = -1;
  554.         } else {
  555.         Tk_GetRootCoords((Tk_Window) winPtr, &winX, &winY);
  556.         Tk_GetRootCoords((Tk_Window) focusPtr, &focusX, &focusY);
  557.         eventPtr->xkey.x -= focusX - winX;
  558.         eventPtr->xkey.y -= focusY - winY;
  559.         }
  560.         eventPtr->xkey.window = focusPtr->window;
  561.         winPtr = focusPtr;
  562.     }
  563.     
  564.     /*
  565.      * Call a grab-related procedure to do special processing on
  566.      * pointer events.
  567.      */
  568.     
  569.     if (mask & (ButtonPressMask|ButtonReleaseMask|PointerMotionMask
  570.         |EnterWindowMask|LeaveWindowMask)) {
  571.         if (mask & (ButtonPressMask|ButtonReleaseMask)) {
  572.         winPtr->dispPtr->lastEventTime = eventPtr->xbutton.time;
  573.         } else if (mask & PointerMotionMask) {
  574.         winPtr->dispPtr->lastEventTime = eventPtr->xmotion.time;
  575.         } else {
  576.         winPtr->dispPtr->lastEventTime = eventPtr->xcrossing.time;
  577.         }
  578.         if (TkPointerEvent(eventPtr, winPtr) == 0) {
  579.         return;
  580.         }
  581.     }
  582.     }
  583.  
  584. #ifdef TK_USE_INPUT_METHODS
  585.     /*
  586.      * Pass the event to the input method(s), if there are any, and
  587.      * discard the event if the input method(s) insist.  Create the
  588.      * input context for the window if it hasn't already been done
  589.      * (XFilterEvent needs this context).
  590.      */
  591.  
  592.     if (!(winPtr->flags & TK_CHECKED_IC)) {
  593.     if (winPtr->dispPtr->inputMethod != NULL) {
  594.         winPtr->inputContext = XCreateIC(
  595.             winPtr->dispPtr->inputMethod, XNInputStyle,
  596.             XIMPreeditNothing|XIMStatusNothing,
  597.             XNClientWindow, winPtr->window,
  598.             XNFocusWindow, winPtr->window, NULL);
  599.     }
  600.     winPtr->flags |= TK_CHECKED_IC;
  601.     }
  602.     if (XFilterEvent(eventPtr, None)) {
  603.         return;
  604.     }
  605. #endif /* TK_USE_INPUT_METHODS */
  606.  
  607.     /*
  608.      * For events where it hasn't already been done, update the current
  609.      * time in the display.
  610.      */
  611.  
  612.     if (eventPtr->type == PropertyNotify) {
  613.     winPtr->dispPtr->lastEventTime = eventPtr->xproperty.time;
  614.     }
  615.  
  616.     /*
  617.      * There's a potential interaction here with Tk_DeleteEventHandler.
  618.      * Read the documentation for pendingPtr.
  619.      */
  620.  
  621.     ip.eventPtr = eventPtr;
  622.     ip.winPtr = winPtr;
  623.     ip.nextHandler = NULL;
  624.     ip.nextPtr = pendingPtr;
  625.     pendingPtr = &ip;
  626.     if (mask == 0) {
  627.     if ((eventPtr->type == SelectionClear)
  628.         || (eventPtr->type == SelectionRequest)
  629.         || (eventPtr->type == SelectionNotify)) {
  630.         TkSelEventProc((Tk_Window) winPtr, eventPtr);
  631.     } else if ((eventPtr->type == ClientMessage)
  632.         && (eventPtr->xclient.message_type ==
  633.             Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS"))) {
  634.         TkWmProtocolEventProc(winPtr, eventPtr);
  635.     }
  636.     } else {
  637.     for (handlerPtr = winPtr->handlerList; handlerPtr != NULL; ) {
  638.         if ((handlerPtr->mask & mask) != 0) {
  639.         ip.nextHandler = handlerPtr->nextPtr;
  640.         (*(handlerPtr->proc))(handlerPtr->clientData, eventPtr);
  641.         handlerPtr = ip.nextHandler;
  642.         } else {
  643.         handlerPtr = handlerPtr->nextPtr;
  644.         }
  645.     }
  646.  
  647.     /*
  648.      * Pass the event to the "bind" command mechanism.  But, don't
  649.      * do this for SubstructureNotify events.  The "bind" command
  650.      * doesn't support them anyway, and it's easier to filter out
  651.      * these events here than in the lower-level procedures.
  652.      */
  653.  
  654.     if ((ip.winPtr != None) && (mask != SubstructureNotifyMask)) {
  655.         TkBindEventProc(winPtr, eventPtr);
  656.     }
  657.     }
  658.     pendingPtr = ip.nextPtr;
  659. }
  660.  
  661. /*
  662.  *----------------------------------------------------------------------
  663.  *
  664.  * TkXFileProc --
  665.  *
  666.  *    This procedure is a file handler created by calling
  667.  *    Tk_CreateFileHandler2.  It is invoked by Tk_DoOneEvent to see
  668.  *    whether an X server connection is ready and, if so, handle
  669.  *    an incoming event on it.
  670.  *
  671.  * Results:
  672.  *    Returns TK_FILE_HANDLED if an event was found and processed.
  673.  *    Otherwise returns TK_READABLE is X events are enabled, or 0 if
  674.  *    flags doesn't contain TK_X_EVENTS, which means that we shouldn't
  675.  *    even look for X events.
  676.  *
  677.  * Side effects:
  678.  *    Could be arbitrary, depending on what happens as a consequence
  679.  *    of invoking an event handler.
  680.  *
  681.  *----------------------------------------------------------------------
  682.  */
  683.  
  684. int
  685. TkXFileProc(clientData, mask, flags)
  686.     ClientData clientData;    /* Pointer to Xlib Display structure
  687.                  * for display. */
  688.     int mask;            /* OR-ed combination of the bits TK_READABLE,
  689.                  * TK_WRITABLE, and TK_EXCEPTION, indicating
  690.                  * current state of file. */
  691.     int flags;            /* Flag bits passed to Tk_DoOneEvent;
  692.                  * contains bits such as TK_DONT_WAIT,
  693.                  * TK_X_EVENTS, Tk_FILE_EVENTS, etc. */
  694. {
  695.     Display *display = (Display *) clientData;
  696.     XEvent event;
  697.  
  698.     if (!(flags & TK_X_EVENTS)) {
  699.     return 0;
  700.     }
  701.     XFlush(display);
  702.     if (mask & TK_READABLE) {
  703.     if (XEventsQueued(display, QueuedAfterReading) == 0) {
  704.  
  705.         /*
  706.          * Things are very tricky if there aren't any events
  707.          * readable at this point (after all, there was
  708.          * supposedly data available on the connection).
  709.          * A couple of things could have occurred:
  710.          * 
  711.          * One possibility is that there were only error events
  712.          * in the input from the server.  If this happens,
  713.          * we should return (we don't want to go to sleep
  714.          * in XNextEvent below, since this would block out
  715.          * other sources of input to the process).
  716.          *
  717.          * Another possibility is that our connection to the
  718.          * server has been closed.  This will not necessarily
  719.          * be detected in XEventsQueued (!!), so if we just
  720.          * return then there will be an infinite loop.  To
  721.          * detect such an error, generate a NoOp protocol
  722.          * request to exercise the connection to the server,
  723.          * then return.  However, must disable SIGPIPE while
  724.          * sending the request, or else the process will die
  725.          * from the signal and won't invoke the X error
  726.          * function to print a nice message.
  727.          */
  728.  
  729.         void (*oldHandler)();
  730.  
  731.         oldHandler = (void (*)()) signal(SIGPIPE, SIG_IGN);
  732.         XNoOp(display);
  733.         XFlush(display);
  734.         (void) signal(SIGPIPE, oldHandler);
  735.         return TK_FILE_HANDLED;
  736.     }
  737.     if (restrictProc != NULL) {
  738.         if (!XCheckIfEvent(display, &event, restrictProc, restrictArg)) {
  739.         return TK_FILE_HANDLED;
  740.         }
  741.     } else {
  742.         XNextEvent(display, &event);
  743.     }
  744.     } else {
  745.     if (QLength(display) == 0) {
  746.         return TK_READABLE;
  747.     }
  748.     if (restrictProc != NULL) {
  749.         if (!XCheckIfEvent(display, &event, restrictProc, restrictArg)) {
  750.         return TK_READABLE;
  751.         }
  752.     } else {
  753.         XNextEvent(display, &event);
  754.     }
  755.     }
  756.  
  757.     /*
  758.      * Got an event.  Deal with mouse-motion-collapsing and
  759.      * event-delaying here.  If there's already an event delayed,
  760.      * then process that event if it's incompatible with the new
  761.      * event (new event not mouse motion, or window changed, or
  762.      * state changed).  If the new event is mouse motion, then
  763.      * don't process it now;  delay it until later in the hopes
  764.      * that it can be merged with other mouse motion events
  765.      * immediately following.
  766.      */
  767.  
  768.     if (tkEventDebug) {
  769.     eventTrace[traceIndex] = event;
  770.     traceIndex = (traceIndex+1) % TK_NEVENTS;
  771.     }
  772.  
  773.     if (tkDelayedEventProc != NULL) {
  774.     if (((event.type != MotionNotify)
  775.             && (event.type != GraphicsExpose)
  776.             && (event.type != NoExpose)
  777.             && (event.type != Expose))
  778.         || (event.xmotion.display
  779.             != delayedMotionEvent.xmotion.display)
  780.         || (event.xmotion.window
  781.             != delayedMotionEvent.xmotion.window)) {
  782.         XEvent copy;
  783.  
  784.         /*
  785.          * Must copy the event out of delayedMotionEvent before
  786.          * processing it, in order to allow recursive calls to
  787.          * Tk_DoOneEvent as part of the handler.
  788.          */
  789.  
  790.         copy = delayedMotionEvent;
  791.         tkDelayedEventProc = NULL;
  792.         Tk_HandleEvent(©);
  793.     }
  794.     }
  795.     if (event.type == MotionNotify) {
  796.     delayedMotionEvent = event;
  797.     tkDelayedEventProc = DelayedEventProc;
  798.     } else {
  799.     Tk_HandleEvent(&event);
  800.     }
  801.     return TK_FILE_HANDLED;
  802. }
  803.  
  804. /*
  805.  *----------------------------------------------------------------------
  806.  *
  807.  * Tk_RestrictEvents --
  808.  *
  809.  *    This procedure is used to globally restrict the set of events
  810.  *    that will be dispatched.  The restriction is done by filtering
  811.  *    all incoming X events through a procedure that determines
  812.  *    whether they are to be processed immediately or deferred.
  813.  *
  814.  * Results:
  815.  *    The return value is the previous restriction procedure in effect,
  816.  *    if there was one, or NULL if there wasn't.
  817.  *
  818.  * Side effects:
  819.  *    From now on, proc will be called to determine whether to process
  820.  *    or defer each incoming X event.
  821.  *
  822.  *----------------------------------------------------------------------
  823.  */
  824.  
  825. Tk_RestrictProc *
  826. Tk_RestrictEvents(proc, arg, prevArgPtr)
  827.     Tk_RestrictProc *proc;    /* X "if" procedure to call for each
  828.                  * incoming event.  See "XIfEvent" doc.
  829.                  * for details. */
  830.     char *arg;            /* Arbitrary argument to pass to proc. */
  831.     char **prevArgPtr;        /* Place to store information about previous
  832.                  * argument. */
  833. {
  834.     Bool (*prev)  _ANSI_ARGS_((Display *display, XEvent *eventPtr, char *arg));
  835.  
  836.     prev = restrictProc;
  837.     *prevArgPtr = restrictArg;
  838.     restrictProc = proc;
  839.     restrictArg = arg;
  840.     return prev;
  841. }
  842.  
  843. /*
  844.  *--------------------------------------------------------------
  845.  *
  846.  * Tk_MainLoop --
  847.  *
  848.  *    Call Tk_DoOneEvent over and over again in an infinite
  849.  *    loop as long as there exist any main windows.
  850.  *
  851.  * Results:
  852.  *    None.
  853.  *
  854.  * Side effects:
  855.  *    Arbitrary;  depends on handlers for events.
  856.  *
  857.  *--------------------------------------------------------------
  858.  */
  859.  
  860. void
  861. Tk_MainLoop()
  862. {
  863.     while (tk_NumMainWindows > 0) {
  864.     Tk_DoOneEvent(0);
  865.     }
  866. }
  867.  
  868. /*
  869.  *--------------------------------------------------------------
  870.  *
  871.  * TkEventDeadWindow --
  872.  *
  873.  *    This procedure is invoked when it is determined that
  874.  *    a window is dead.  It cleans up event-related information
  875.  *    about the window.
  876.  *
  877.  * Results:
  878.  *    None.
  879.  *
  880.  * Side effects:
  881.  *    Various things get cleaned up and recycled.
  882.  *
  883.  *--------------------------------------------------------------
  884.  */
  885.  
  886. void
  887. TkEventDeadWindow(winPtr)
  888.     TkWindow *winPtr;        /* Information about the window
  889.                  * that is being deleted. */
  890. {
  891.     register TkEventHandler *handlerPtr;
  892.     register InProgress *ipPtr;
  893.  
  894.     /*
  895.      * While deleting all the handlers, be careful to check for
  896.      * Tk_HandleEvent being about to process one of the deleted
  897.      * handlers.  If it is, tell it to quit (all of the handlers
  898.      * are being deleted).
  899.      */
  900.  
  901.     while (winPtr->handlerList != NULL) {
  902.     handlerPtr = winPtr->handlerList;
  903.     winPtr->handlerList = handlerPtr->nextPtr;
  904.     for (ipPtr = pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
  905.         if (ipPtr->nextHandler == handlerPtr) {
  906.         ipPtr->nextHandler = NULL;
  907.         }
  908.         if (ipPtr->winPtr == winPtr) {
  909.         ipPtr->winPtr = None;
  910.         }
  911.     }
  912.     ckfree((char *) handlerPtr);
  913.     }
  914. }
  915.  
  916. /*
  917.  *----------------------------------------------------------------------
  918.  *
  919.  * TkCurrentTime --
  920.  *
  921.  *    Try to deduce the current time.  "Current time" means the time
  922.  *    of the event that led to the current code being executed, which
  923.  *    means the time in the most recently-nested invocation of
  924.  *    Tk_HandleEvent.
  925.  *
  926.  * Results:
  927.  *    The return value is the time from the current event, or
  928.  *    CurrentTime if there is no current event or if the current
  929.  *    event contains no time.
  930.  *
  931.  * Side effects:
  932.  *    None.
  933.  *
  934.  *----------------------------------------------------------------------
  935.  */
  936.  
  937. Time
  938. TkCurrentTime(dispPtr)
  939.     TkDisplay *dispPtr;        /* Display for which the time is desired. */
  940. {
  941.     register XEvent *eventPtr;
  942.  
  943.     if (pendingPtr == NULL) {
  944.     return dispPtr->lastEventTime;
  945.     }
  946.     eventPtr = pendingPtr->eventPtr;
  947.     switch (eventPtr->type) {
  948.     case ButtonPress:
  949.     case ButtonRelease:
  950.         return eventPtr->xbutton.time;
  951.     case KeyPress:
  952.     case KeyRelease:
  953.         return eventPtr->xkey.time;
  954.     case MotionNotify:
  955.         return eventPtr->xmotion.time;
  956.     case EnterNotify:
  957.     case LeaveNotify:
  958.         return eventPtr->xcrossing.time;
  959.     case PropertyNotify:
  960.         return eventPtr->xproperty.time;
  961.     }
  962.     return dispPtr->lastEventTime;
  963. }
  964.  
  965. /*
  966.  *----------------------------------------------------------------------
  967.  *
  968.  * DelayedEventProc --
  969.  *
  970.  *    This procedure is a bit of a kludge;  it is called by
  971.  *    Tk_DoOneEvent just before calling idle handlers, if there
  972.  *    is a delayed mouse motion event.
  973.  *
  974.  * Results:
  975.  *    None.
  976.  *
  977.  * Side effects:
  978.  *    The mouse motion event gets processed.
  979.  *
  980.  *----------------------------------------------------------------------
  981.  */
  982.  
  983. static void
  984. DelayedEventProc()
  985. {
  986.     XEvent copy;
  987.  
  988.     /*
  989.      * Special trick:  must free delayedMotionEvent *before* calling
  990.      * Tk_HandleEvent, so that the event handler can call Tk_DoOneEvent
  991.      * recursively without infinite looping.
  992.      */
  993.  
  994.     copy = delayedMotionEvent;
  995.     tkDelayedEventProc = NULL;
  996.     Tk_HandleEvent(©);
  997. }
  998.